home *** CD-ROM | disk | FTP | other *** search
/ The Original Shareware 1.1 / The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso / 38 / sgn_bans.zip / PRINTPAK.PAS < prev    next >
Pascal/Delphi Source File  |  1985-12-17  |  22KB  |  632 lines

  1. { PrintPak.Pas }
  2. { Print very high-res graphics on EPSON FX-80/RX-80 }
  3.  
  4. { by  Bryan B. Smith }
  5. { Based on ideas from Byte, Nov. 1985, pp. 219-232
  6.  
  7. { Storage order in bitmap arrays has been reversed so that bytes are }
  8. { stored in the sequence in which they are to be printed.  This permits }
  9. { high-speed graphics printing by the procedure PrintBlock. }
  10. { Effective size of the arrays has been increased beyond the max. permitted }
  11. { 64K by use of an array of pointers to 64K arrays. }
  12.  
  13. { START GLOBAL DECLARATIONS ... }
  14.  
  15. {$V-}               { prohibits compiler checks of string size }
  16.  
  17. const
  18.   across = 1599 ;
  19.   down = 39 ; { change next line also }
  20.   DownPlusOne = 40 ;
  21.   LastLineDown = 1279 ; { (39+1)*2*16 - 1 }
  22.   hsteps = 9.4504 ; { horizontal steps/mm, fount by experiment }
  23.   vsteps = 5.9167 ; { vertical   steps/mm, found by experiment }
  24.   leftmarg : integer = 7 ; { extra left-marg. space needed to centre }
  25.   NumberOfFonts = 4 ;
  26.  
  27. type
  28.   data_type = array[0..down,0..across] of byte ;
  29.   mask_array = array[0..7] of byte ;
  30.   word_mask_array = array[0..15] of integer ;
  31.   Font1Descrip = array[0..255,0..7] of byte ;
  32.   Font2Descrip = array[0..255,0..13] of byte ;
  33.   Font3Descrip = array[0..255,0..13] of integer ;
  34.   Font4Descrip = array[0..255,0..27] of integer ;
  35.   utilitystr = string[80] ;
  36.  
  37. const
  38.   M : mask_array = ($80,$40,$20,$10,$08,$04,$02,$01) ; { +ve mask }
  39.   R : mask_array = ($7F,$BF,$DF,$EF,$F7,$FB,$FD,$FE) ; { "not" M }
  40.   WM : word_mask_array =
  41.            ($8000,$4000,$2000,$1000,$0800,$0400,$0200,$0100,
  42.             $0080,$0040,$0020,$0010,$0008,$0004,$0002,$0001) ;
  43.  
  44. Var
  45.   EvenMap, OddMap : array[0..1] of ^data_type ;
  46.   lowest : integer ;
  47.   halfwid, halfht : integer ;
  48.   Font1 : Font1Descrip ;
  49.   Font2 : Font2Descrip ;
  50.   Font3 : Font3Descrip ;
  51.   Font4 : Font4Descrip ;
  52.   Font1File : file of Font1Descrip ;
  53.   Font2File : file of Font2Descrip ;
  54.   Font3File : file of Font3Descrip ;
  55.   Font4File : file of Font4Descrip ;
  56.   WantScreen, WantPrint, FirstCharOnly, CheckTextOnly : boolean ;
  57.   Landscape, grid : boolean ;
  58.   WantFont : array[1..NumberOfFonts] of boolean ;
  59.  
  60. { END of global declarations }
  61.  
  62. Procedure DrawGrid ; { Draws a grid - of course ! }
  63.  
  64. Var
  65.   j, k, x,y,xstart,xend,xinc,ystart,yend,yinc : integer ;
  66.  
  67. begin
  68.   plot(0,0,1) ;
  69.   plot(0,199,1) ; 
  70.   for j := 1 to 12 do
  71.   begin
  72.     x := j * 50 ;
  73.     plot(x,0,1) ;
  74.     plot(x,199,1) ;  
  75.     for k := 1 to 16 do
  76.     begin
  77.       if landscape
  78.         then y := trunc(k*12.5) - 1
  79.         else y := trunc((k-1)*12.5) ;
  80.       plot(x,y,1) ;
  81.       plot(0,y,1) ;     { lots of repeated points here ... }
  82.       plot(639,y,1) ;   {      but no detectable time lost }
  83.     end ; { k } 
  84.   end ; { j }
  85. end ;  { DrawGrid }
  86.  
  87. Procedure Init_PrintPak ;
  88.  
  89. { Performs the initialization chores }
  90.  
  91. Var
  92.   i, j : integer ;
  93.   prefix : string[20] ;
  94.   fontname : string[30] ;
  95.  
  96.  
  97. begin
  98.   if WantPrint then { Don't allocate bitmap space if not required. }
  99.                     { That way, can debug on screen on machine with }
  100.                     { small memory. }
  101.   begin
  102.     for j := 0 to 1 do
  103.     begin
  104.       new(EvenMap[j]) ;  { set aside memory space for arrays }
  105.       new( OddMap[j]) ;
  106.       { fillchar is infinitely faster than code in BYTE - BBS ... }
  107.       fillchar(EvenMap[j]^[0,0], (across+1)*(down+1), 0 ) ;
  108.       fillchar( OddMap[j]^[0,0], (across+1)*(down+1), 0 ) ;
  109.     end ; { for }
  110.   end ; { if WantPrint }
  111.  
  112.   lowest  := (down+1)*8*2*2 - 1 ;  { line number of lowest printer line }
  113.   halfwid := (across+1) div 2 ;   { # cols in left half of printout }
  114.   halfht  := (lowest+1) div 2 ;   { # lines in top half of printout }
  115.  
  116.   SearchEnvironment('SSMF',Prefix) ; { Gets disk prefix or null string }
  117.  
  118.   If WantFont[1] then
  119.   begin
  120.     FontName := Prefix + 'FONT1.FNT' ;
  121.     Assign(Font1File,FontName) ;
  122.     {$I-}
  123.     reset(Font1File) ;
  124.     {$I+}
  125.     if IOresult <> 0 then
  126.     begin
  127.       writeln('Can not find font file ',FontName) ;
  128.       halt ;
  129.     end ;
  130.     ClrScr ;
  131.     Writeln('Loading Font 1') ;
  132.     read(Font1File,Font1) ;
  133.   end ; { font 1 }
  134.  
  135.   If WantFont[2] then
  136.   begin
  137.     FontName := Prefix + 'FONT2.FNT' ;
  138.     Assign(Font2File,FontName) ;
  139.     {$I-}
  140.     reset(Font2File) ;
  141.     {$I+}
  142.     if IOresult <> 0 then
  143.     begin
  144.       writeln('Can not find font file ',FontName) ;
  145.       halt ;
  146.     end ;
  147.     Writeln('Loading Font 2') ;
  148.     read(Font2File,Font2) ;
  149.   end ; { font 2 }
  150.  
  151.   If WantFont[3] then
  152.   begin
  153.     FontName := Prefix + 'FONT3.FNT' ;
  154.     Assign(Font3File,FontName) ;
  155.     {$I-}
  156.     reset(Font3File) ;
  157.     {$I+}
  158.     if IOresult <> 0 then
  159.     begin
  160.       writeln('Can not find font file ',FontName) ;
  161.       halt ;
  162.     end ;
  163.     Writeln('Loading Font 3') ;
  164.     read(Font3File,Font3) ;
  165.   end ; { font 3 }
  166.  
  167.   if WantFont[4] then
  168.   begin
  169.     FontName := Prefix + 'FONT4.FNT' ;
  170.     Assign(Font4File,FontName) ;
  171.     {$I-}
  172.     reset(Font4File) ;
  173.     {$I+}
  174.     if IOresult <> 0 then
  175.     begin
  176.       writeln('Can not find font file ',FontName) ;
  177.       halt ;
  178.     end ;
  179.     Writeln('Loading Font 4') ;
  180.     read(Font4File,Font4) ;
  181.   end ; { font 4 }
  182.  
  183. end ; { Init_PrintPak }
  184.  
  185. Procedure PrintBlock(var a ; SizeInBytes: integer ) ;
  186. {
  187. This procedure was needed because the print method used in Byte,
  188. i.e. 1600 calls to "write" per print line, had so much overhead
  189. that the CPU could not keep up with the printer, and so the
  190. printer moved in a series of prints and backups across each line.
  191. In contrast, this assembler code has low enough overhead that the
  192. printer runs continuously.
  193.  
  194. The procedure will print either bytes or chars.  The number of
  195. bytes/chars printed is limited to MaxInt.  Note that "a" does not
  196. have any type in the argument list, and that it must be declared
  197. "var".  Having no type, it may not be used in the procedure,
  198. except as an absolute address.
  199.  
  200. Note - providing LST with a 2K buffer, e.g.
  201.   Var List : Text[$800] ;
  202.   assign(List,'LST:') ;
  203.   write(List,...
  204. did NOT prevent the print-and-backup action of the printer.
  205. }
  206.  
  207. Var
  208.   aa : array[1..maxint] of byte absolute a ;
  209.   aseg,aofs : integer ;
  210.  
  211. begin
  212.   aseg := seg(aa) ; aofs := ofs(aa) ;
  213.   inline(
  214.       { Set up for the big print ... }
  215.     $8B/$8E/SizeInBytes/   { MOV CX,SizeInBytes[BP] }
  216.     $8B/$BE/Aofs/          { MOV DI,Aofs[BP] }
  217.     $8E/$86/Aseg/          { MOV ES,Aseg[BP] }
  218.       { Now Print it ... }
  219.     $83/$F9/$00/           { AGAIN : CMP CX,0 }
  220.     $74/$0B/               { JE  DONE }
  221.     $26/$8A/$05/           { MOV AL,ES:[DI] }
  222.     $32/$E4/               { XOR AH,AH }
  223.     $CD/$17/               { INT 17H }
  224.     $49/                   { DEC CX }
  225.     $47/                   { INC DI }
  226.     $EB/$F0/               { JMP AGAIN }
  227.     $90                    { DONE: NOP}
  228.   ) ;
  229. end ;
  230.  
  231. Procedure Printout ;  { Output to Epson FX-type printer }
  232.  
  233. { Prints out the 256KB (almost) bit map.  That's almost 2 million }
  234. { bits - or printhead wires.  Had to modify the original source a }
  235. { a lot so we did not waste printer time on empty right ends of   }
  236. { lines, and so that the CPU could keep up with the printer !     }
  237.  
  238. { Remember that even though this uses quad-density graphics, the  }
  239. { Epson printer will not print 2 successive dots on the same row. }
  240.  
  241. Var
  242.   n_lo, n_hi : byte ;
  243.   i, j, last, part : integer ;
  244.   ch : char ;
  245.  
  246. label EmptyLine ;
  247.  
  248. begin { Printout }
  249.   write(LST,#27,'8') ; { single-sheet mode }
  250.   { take any backlash out of printer linefeed mechanism ... }
  251.   write(LST,chr(13)) ; { car. return }
  252.   write(LST,chr(27),'3',chr(1)) ;  { set linefeed for 1/3 dot down }
  253.   for j := 1 to 10 do write(LST,chr(10)) ;             { linefeeds }
  254. {
  255.   Since CPU is faster than printer, we will invest a little CPU time to
  256.   avoid printing empty trailing portion of line.  BBS
  257. }
  258.   for part := 0 to 1 do  { for two halves of page }
  259.   begin
  260.     for j := 0 to 39 do  { for 40 lines in each half page }
  261.     begin
  262.       if keypressed then { check if user wants out }
  263.       begin
  264.         read(KBD,ch) ; { get rid of keystroke }
  265.         write(LST,#12,#27,'2') ; { formfeed and reset linefeed to 1/6 " }
  266.         write(LST,#27,'9') ;     { end single-sheet mode }
  267.         TextMode(BW80) ;
  268.         ClrScr ;
  269.         halt ;
  270.       end ; { if keypressed }
  271.       last := across + 1 ;
  272.       repeat
  273.         last := last -1 ;
  274.         if last = -1 then goto EmptyLine ;
  275.       until (EvenMap[part]^[j,last] <> 0)
  276.                or (OddMap[part]^[j,last] <> 0) ;
  277.  
  278. EmptyLine :
  279.       if last > -1 then
  280.       begin
  281.         n_hi := (last+1) div 256 ; { part of number of graphic bytes coming }
  282.         n_lo := (last+1) mod 256 ; { rest of number of graphic bytes coming }
  283.         { spaces at start ofline act as filler, then enter }
  284.         { graphics mode, giving number of graphic bytes coming }
  285.         write(LST,'':LeftMarg,#27,'Z',chr(n_lo),chr(n_hi)) ;
  286.         {"last" is index of the last non-zero byte: but first has index 0 ... }
  287.         printblock(EvenMap[part]^[j,0],last+1) ;  { print even row }
  288.         write(LST,#13) ;           { carriage return }
  289.       end ;
  290.       write(LST,#27,'3',#1) ;      { set linefeed for 1/3 dot down }
  291.       write(LST,#10) ;             { linefeed }
  292.       if last > -1 then
  293.       begin
  294.         write(LST,'':LeftMarg,#27,'Z',chr(n_lo),chr(n_hi)) ;
  295.                                     { ... graphics mode again }
  296.         printblock(OddMap[part]^[j,0],last+1) ;  { print odd row }
  297.         write(LST,#13) ;            { carriage return }
  298.       end ;
  299.       write(LST,#27,'3',#22) ;      { set linefeed for 7 1/3 dot down }
  300.       write(LST,#10) ;              { linefeed }
  301.     end ; { for j }
  302.   end ; { for part }
  303.   writeln(LST,' ') ;
  304.   write(LST,#12,#27,'2') ; { formfeed and reset linefeed to 1/6 " }
  305.   write(LST,#27,'9') ; { end single-sheet mode }
  306.  
  307. end ; { Printout }
  308.  
  309. Procedure Pset1(xin,yin : integer ) ;
  310.  
  311. { writes the dot at position (xin,yin) into memory arrays }
  312.  
  313. { This modification of Pset that always uses colour = 1, and has the
  314. { (cleaned-up) Change procedure moved in-line to cut down on call
  315. { overheads.
  316. { An equivalent of Plot(x,y,1) has also been moved in-line for
  317. { speed-up purposes. }
  318.  
  319. Var
  320.   I, line, height, part, x, y, old, Xforplot, Yforplot : integer ;
  321.   odd,offset,t : integer ;
  322.  
  323. begin { Pset1 }
  324.   if landscape
  325.     then
  326.     begin
  327.       x := yin ; y := lastlinedown - xin ;
  328.       { LastLineDown - y is same as xin, so ... }
  329.       Xforplot := xin shr 1 ;   { xin div 2 }
  330.       YforPlot := x shr 3 ;     { x div 8 }
  331.     end
  332.     else  { portrait mode }
  333.     begin
  334.       x := xin ; y := yin ;
  335.       Xforplot := y shr 1 ;
  336.       Yforplot := 200 - (x shr 3) ;
  337.              { portrait mode sideways }
  338.     end ; { if landscape else portrait }
  339.  
  340.   if WantScreen then
  341.   begin
  342.  
  343.     { here, for speed, we will put the code to write point directly to
  344.     { the graphics screen directly in line rather than take the overhead
  345.     { of a procedure call.  This was not done in DrawGrid because speed 
  346.     { was not such a consideration there. }
  347.     { This change cut draw time for an example from 27 down to 20 secs. }
  348.  
  349.     if (Yforplot and $0001) = 0 then offset := 0   { if even # line }
  350.                                 else offset := $2000 ;  { else odd # }
  351.     offset := offset + 80*(Yforplot shr 1)
  352.                      + Xforplot shr 3 ; { x divided by 8, y by 2 }
  353.     t := mem[$B800:offset] or M[Xforplot and $0007] ; { remaind from div by 8 }
  354.     mem[$B800:offset] := lo(t) ;
  355.   end ;
  356.  
  357.   if WantPrint then
  358.   begin
  359.                                  { vertical position of pixel consists of a }
  360.                                  { line # between 0 and down; and a height }
  361.                                  { between 0 and 15, divided into }
  362.                                  { even-odd groups }
  363.     Line := Y shr 4 ; { maybe faster than := Y div 16 ; }
  364.     height := (Y and $000F) shr 1 ; { same as := (Y mod 16) div 2 ; }
  365.     if Line <= down then part := 0    { in first  set of arrays }
  366.                     else part := 1 ;  { in second set of arrays }
  367.     line := line mod (DownPlusOne) ; { line number within lower map, maybe }
  368.     if y mod 2 = 0 then
  369.     begin  { even line }
  370.       Old := EvenMap[part]^[line,x] ;
  371.       EvenMap[part]^[line,x] := lo(old or M[height]) ;
  372.       { insert set bit in place }
  373.     end
  374.     else   { odd line }
  375.     begin
  376.       Old := OddMap[part]^[line,x] ;
  377.       OddMap[part]^[line,x] := lo(old or M[height]) ;
  378.     end ;
  379.   end ; { want print }
  380. end ; { Pset1 }
  381.  
  382. Procedure PutChar(var cha ;
  383.                   var xloc : integer ;
  384.                   yloc,FontNumber,DotCols,DotRows,Between : integer ) ;
  385.  
  386. { Puts one character into screen and into printer bit-map. }
  387.  
  388. Var
  389.   ch : byte absolute cha ; { allows printing of any type }
  390.   tb : byte ;
  391.   j,k,mm,n,ti,dck,drj : integer ;
  392.  
  393. begin
  394.   case FontNumber of
  395.  
  396.     1 : begin
  397.       for j := 0 to 7 do
  398.         begin
  399.         tb := font1[ord(ch),j] ;
  400.         for k := 0 to 7 do
  401.           if (tb and M[k]) <> 0 then
  402.           begin                            { each dot is expanded }
  403.             For mm := 0 to DotCols-1 do    { to DotCols columns }
  404.               for n  := 0 to DotRows-1 do  { and DotRows rows }
  405.                 Pset1(xloc+DotCols*k+mm,yloc+DotRows*j+n) ;
  406.         end ; { for k }
  407.       end ; { for j }
  408.       xloc := xloc + (8+Between)*DotCols ;
  409.     end ; { 1 }
  410.  
  411.     2 : begin
  412.       for j := 0 to 13 do
  413.         begin
  414.         tb := font2[ord(ch),j] ;
  415.         for k := 0 to 7 do
  416.           if (tb and M[k]) <> 0 then
  417.           begin                            { each dot is expanded }
  418.             For mm := 0 to DotCols-1 do    { to DotCols columns }
  419.               for n  := 0 to DotRows-1 do  { and DotRows rows }
  420.                 Pset1(xloc+DotCols*k+mm,yloc+DotRows*j+n) ;
  421.         end ; { for k }
  422.       end ; { for j }
  423.       xloc := xloc + (9+Between)*DotCols ;
  424.     end ; { 2 }
  425.  
  426.     3 : begin
  427.       drj := 0 ;                           { drj is DotRows * j }
  428.       for j := 0 to 13 do
  429.         begin
  430.         ti := font3[ord(ch),j] ;
  431.         dck := 0 ;                         { dck is DotCols * k }
  432.         for k := 0 to 15 do
  433.         begin
  434.           if (ti and WM[k]) <> 0 then
  435.           begin                            { each dot is expanded }
  436.             For mm := 0 to DotCols-1 do    { to DotCols columns }
  437.               for n  := 0 to DotRows-1 do  { and DotRows rows }
  438.                 Pset1(xloc+dck+mm,yloc+drj+n) ;
  439.           end ; { if }
  440.           dck := dck + DotCols ;
  441.         end ; { k }
  442.         drj := drj + DotRows ;
  443.       end ; { for j }
  444.       xloc := xloc + (18+Between)*DotCols ;
  445.       { explanation - for fonts 3 & 4, Putstring passes a temporarily }
  446.       { doubled copy of Between to PutChar }
  447.     end ; { 3 }
  448.  
  449.     4 : begin
  450.       drj := 0 ;                           { drj is DotRows * j }
  451.       for j := 0 to 27 do
  452.         begin
  453.         ti := font4[ord(ch),j] ;
  454.         dck := 0 ;                         { dck is DotCols * k }
  455.         for k := 0 to 15 do
  456.         begin
  457.           if (ti and WM[k]) <> 0 then
  458.           begin                            { each dot is expanded }
  459.             For mm := 0 to DotCols-1 do    { to DotCols columns }
  460.               for n  := 0 to DotRows-1 do  { and DotRows rows }
  461.                 Pset1(xloc+dck+mm,yloc+drj+n) ;
  462.           end ; { if }
  463.           dck := dck + DotCols ;
  464.         end ; { k }
  465.         drj := drj + DotRows ;
  466.       end ; { for j }
  467.       xloc := xloc + (18+Between)*DotCols ;
  468.       { explanation - see 3 }
  469.     end ; { 4 }
  470.  
  471.   end ; { of cases }
  472. end ; { PutChar }
  473.  
  474. Procedure PutString(Strng : utilitystr ;
  475.                     xl : integer ;
  476.                     var yloc : integer ;
  477.                     FontNumber,DotCols,DotRows,Between : integer ;
  478.                     centre,PrePass : boolean ;
  479.                     var OffPage : boolean) ;
  480.  
  481. { Puts a whole string onto screen and into printer bit map. }
  482.  
  483. { Between is number of extra spaces between chars. }
  484. { if centre is true, the line is centred, and xl is ignored. }
  485. { on return, yloc is increased by twice the line height to provide }
  486. { auto line spacing for next line. }
  487. { if PrePass is true, will not print to screen or printer, but }
  488. { merely verify string fits on screen. }
  489.  
  490. Var
  491.   j, xloc, xsize, ysize, LowestLine, YOffset, emptyx : integer ;
  492.   temp, ti, lenstr : integer ;
  493.   pct : real ;
  494.   ch : char ;
  495.   fault : boolean ;
  496.  
  497. const
  498.   ln : string[6] = 'Line *' ;
  499.   isoff : string[19] = '* is off page at ' ;
  500. begin
  501.   case FontNumber of
  502.     1 : begin
  503.           xsize  := 8 ;     { 8 columns per char., all bitmapped }
  504.           ysize  := 8 ;     { 8 rows in bitmap }
  505.           LowestLine := 7 ; { bottom of 'H' is line 7 of char. bitmap }
  506.           emptyx := 0 ;      { all 8 cols mapped }
  507.         end ; { 1 }
  508.     2 : begin
  509.           xsize  := 9 ;  { use 9 columns/char., 8 bitmapped + 1 blank }
  510.           ysize  := 14 ; { 14 rows in bitmap }
  511.           LowestLine := 11 ;
  512.           emptyx := 1 ;   { 1 empty col in xsize }
  513.         end ; { 2 }
  514.     3 : begin
  515.           xsize := 18 ;  { like font 2, but twice as wide }
  516.           ysize := 14 ;
  517.           LowestLine := 11 ;
  518.           Between := 2*Between ;  { this is only a temp copy of between }
  519.           emptyx := 2     { 2 empty cols in xsize }
  520.         end ; { 3 }
  521.     4 : begin
  522.           xsize := 18 ;
  523.           ysize := 28 ;  { like font 3, but twice as high }
  524.           LowestLine := 22 ;
  525.           Between := 2*Between ;  { only a temp. copy of Between }
  526.           emptyx := 2 ;
  527.         end ; { 4 }
  528.   end ; { case }
  529.   xsize  := (xsize + Between) * DotCols ;
  530.   emptyx := emptyx * DotCols ;
  531.   ysize  := ysize * DotRows ;
  532.          { xloc will be altered by PutChar ...}
  533.          { so make an alterable copy }
  534.          { if centre is true, then the x-location xl is ignored }
  535.  
  536.   { allow for indexing by bottom print line of character like H or M }
  537.   { this should make it much easier to get 2 sizes of type on same line }
  538.   { Add 1 to allow user's y scale to start at 1, programs to start at 0 }
  539.   { i.e. YOffset := LowestLine*DotRows - 1 + 1 }
  540.   yoffset := LowestLine*DotRows ;
  541.   if centre
  542.        then
  543.        if landscape
  544.          then xloc := (LastLineDown - (length(Strng)*(xsize)
  545.                           - Between*DotCols { don't count 1 space }
  546.                           - emptyx))        { don't count non-print col }
  547.                             div 2
  548.          else xloc := (across - (length(Strng)*(xsize)
  549.                           - Between*DotCols
  550.                           - emptyx)) div 2
  551.        else xloc := xl - 1 ; { -1 to allow user to use scale starting at 1 }
  552.                              {    whereas program scale starts at 0 }
  553.  
  554.   { because PutChar indexes by TOP ROW of char, make temp. change in yloc }
  555.   yloc := yloc - yoffset ;
  556.  
  557.   if PrePass
  558.   then
  559.   begin
  560.     OffPage := false ;
  561.     if yloc < 0 then
  562.     begin
  563.       temp := -yloc ;
  564.       { 16. = 1600/100, 12.8 = 1280/100 }
  565.       if landscape then pct := temp/16. else pct := temp/12.8 ;
  566.       Writeln(ln,Strng,isoff,'top.') ;
  567.       writeln('by ':8,temp,' rows, ',pct:7:3,' %') ;
  568.       OffPage := true
  569.     end ;
  570.     if landscape
  571. {
  572.       then temp := yloc + (ysize-YOffset) - across
  573.       else temp := yloc + (ysize-YOffset) - LastLineDown ;
  574. }
  575.       then temp := yloc + ysize - across
  576.       else temp := yloc + ysize - LastLineDown ;
  577.     fault := temp > 0 ;
  578.     if fault then
  579.     begin
  580.       if landscape then pct := temp/16. else pct := temp/12.8 ;
  581.       Writeln(ln,Strng,isoff,'bottom.') ;
  582.       writeln('by ':8,temp,' rows, ',pct:7:3,' %') ;
  583.       OffPage := true
  584.     end ;
  585.     if xloc < 0 then
  586.     begin
  587.       temp := -xloc ;
  588.       if landscape then pct := temp/12.8 else pct := temp/16. ;
  589.       Writeln(ln,Strng,isoff,'left.') ;
  590.       writeln('by ':8,temp,' columns, ',pct:7:3,' %') ;
  591.       OffPage := true
  592.     end ;
  593.     ti := xloc + length(strng)*xsize - 1 - Between*DotCols - emptyx ;
  594.     if landscape
  595.       then begin temp := ti - LastLineDown ; pct := temp/12.8 ; end
  596.       else begin temp := ti - across ; pct := temp/16. ; end ;
  597.     fault := temp > 0 ;
  598.     if fault then
  599.     begin
  600.       Writeln(ln,Strng,isoff,'right.') ;
  601.       writeln('by ':8,temp,' columns, ',pct:7:3,' %') ;
  602.       OffPage := true
  603.     end ;
  604.   end
  605.   else { *** NOT PRE-PASS, do the real thing *** }
  606.   begin
  607.     If not WantScreen then writeln('Working on "',Strng,'"') ;
  608.     { maybe we do FirstCharOnly on a nul string ... lenstr = 0 }
  609.     if FirstCharOnly and (length(Strng) > 1) then lenstr := 1
  610.     else lenstr := length(Strng) ;
  611.     for j := 1 to lenstr do
  612.     begin
  613.       if keypressed then { allow user to abort after each character }
  614.       begin
  615.         read(KBD,ch) ; { gobble up the character and ... }
  616.         if (not WantScreen) then begin ClrScr ; halt ; end ;
  617.         gotoxy(1,1) ;
  618.         write('Press any letter to exit.  ') ;
  619.         repeat until keypressed ; { ... wait while user examines screen }
  620.         read(KBD,ch) ; { gobble character }
  621.         TextMode(BW80) ; { reset screen }
  622.         halt ;
  623.       end ;
  624.       ch := Strng[j] ;
  625.       if FirstCharOnly and (ch = ' ') then ch := 'Z' ;
  626.       PutChar(ch,xloc,yloc,FontNumber,DotCols,DotRows,Between) ;
  627.     end ;
  628.   end ; { else }
  629.  
  630.   yloc := yloc + yoffset ; { cancel temporary offset }
  631.   yloc := yloc + 2*ysize ; { feed down by twice cell height }
  632. end ; { PutString }